﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Net;
using System.Text;
using HtmlAgilityPack;
using log4net;

namespace PlayerStat
{
    class ScrapeFR : ScrapeSite
    {
        WebClient wc = new WebClient();

        static int tempNumber = 1;
        ILog logger;

        private int leagueID = -1;
        private string match_url_template = @"http://www.foot-national.com/{0}-{1}-calendrier-{2}-{3}.html";
        private string match_stat_template = @"http://www.foot-national.com/{0}";
        private string league = string.Empty;
        private int maxRound = -1;

        public ScrapeFR()
        {
            logger = LogManager.GetLogger("scrape");

            if (logger == null)
            {
                throw new Exception("Null Logger");
            }
        }

        public void SetLeague(int nLeagueID)
        {
            leagueID = nLeagueID;

            switch (leagueID)
            {
                case 9:
                    league = "ligue1";
                    maxRound = 38;
                    break;
                case 10:
                    league = "ligue2";
                    maxRound = 38;
                    break;
                default:
                    throw new Exception("Wrong League Number");
            }
        }

        public bool Scrape()
        {
            logger.Debug("#Scrape France League# Start");

            HtmlDocument mainDoc = new HtmlDocument();
            HtmlDocument statDoc = new HtmlDocument();

            try
            {
                EventInit evnt = new EventInit(leagueID, maxRound,Leagues.FranceID.ToString());

                string date = string.Empty;
                int round = 0;
                int year = 2007;
                string url = string.Empty;

                while (evnt.GetEventRound(leagueID, out year, out round, out date))
                {
                    logger.DebugFormat("Go to season {0}-{1}, round {2}", year, year + 1, round);

                    url = GetMatchURL(year, round);

                    //mainIE.GoTo(url);
                    logger.Debug("Download match list.");
                    wc.DownloadFile(url, "c:\\Scrape\\MainIE.html");
                    logger.Debug("Goto match list.");
                    mainDoc.Load("c:\\Scrape\\MainIE.html", Encoding.GetEncoding("ISO-8859-1"));

                    //System.Threading.Thread.Sleep(5 * 1000);
                    //Get Match Date

                    DataTable matches = GetMatches(mainDoc, date, round);

                    foreach (DataRow dr in matches.Rows)
                    {
                        MatchData md = new MatchData();
                        md.LeagueID = this.leagueID;
                        md.MatchRound = round;
                        md.matchTime = (DateTime)dr["MatchTime"];
                        md.home = (string)dr["HomeTeam"];
                        md.away = (string)dr["AwayTeam"];
                        md.match.homeScore = (int)dr["HomeScore"];
                        md.match.awayScore = (int)dr["AwayScore"];
                        logger.DebugFormat("Scrape the match({0} : {1} ) data.", new string[] { md.home, md.away });

                        if (md.ExistInDB())
                        {
                            md = null;
                            logger.DebugFormat("Data is already scraped into DB.");
                            continue;
                        }

                        //statIE = new IE();
                        //statIE.GoTo(dr["MatchStatURL"].ToString());
                        logger.Debug("Download match statistic page.");
                        wc.DownloadFile(match_stat_template + dr["MatchStatURL"].ToString(), "C:\\Scrape\\StatIE.html");
                        logger.Debug("Goto match statistic page.");
                        statDoc.Load("C:\\Scrape\\StatIE.html", Encoding.GetEncoding("ISO-8859-1"));
                        //System.Threading.Thread.Sleep(5000);

                        if (!GetMatchStat(statDoc, ref md))
                        {
                            //statIE.Close();
                            logger.ErrorFormat("Error to get statistic in {0} vs {1}", md.home, md.away);
                            continue;
                        }

                        logger.Debug("Save match to DB.");
                        //statIE.Close();
                        md.SaveToDB();

                    }

                    //System.Threading.Thread.Sleep(1000 * 5);
                    evnt.NextRound();
                }

                logger.Debug("#Scrape France League# End");
                return true;
            }
            catch (Exception ex)
            {
                logger.Error("#Scrape France League# Failed: Exception.");
                logger.Error(ex.ToString());
                return false;
            }
            finally
            {
                logger.Debug("#Scrape France League# Finally");
                wc.Dispose();
                mainDoc = null;
                statDoc = null;
            }
        }

        private DataTable GetMatches(HtmlDocument mainDoc, string date, int round)
        {
            HtmlNode table = GetMatchTable(mainDoc);

            if (table == null)
            {
                logger.Error("The validated match table is not found.");
                return null;
            }

            DataTable matches = null;

            if (!GetMatchList(table, date, out matches))
            {
                logger.ErrorFormat("Parse the match list of round {0}.", round);
                //continue;
            }

            logger.DebugFormat("There are {0} mathes in round {1}", matches.Rows.Count, round);
            return matches;
        }

        public string GetMatchURL(int year, int round)
        {
            return string.Format(match_url_template, year, year + 1, league,round);
        }

        private HtmlNode GetMatchTable(HtmlDocument doc)
        {
            HtmlNode table = doc.DocumentNode.SelectSingleNode("//div[@id='calendrier']/table");

            if (table != null && (table.InnerText.IndexOf("RÉSUMÉ") != -1 || table.InnerText.IndexOf("PARIEZ") != -1))
            {
                logger.Debug("#GetMatchTable# Get match table.");
                return table;
            }

            return null;
        }

        private bool GetMatchList(HtmlNode tbl, string date, out DataTable mTable)
        {
            try
            {
                logger.Debug("#GetMatchList# Start");

                DateTime? edt = null;
                DateTime? idt = null;

                mTable = new DataTable();

                mTable.Columns.Add("MatchTime", typeof(DateTime));
                mTable.Columns.Add("HomeTeam", typeof(string));
                mTable.Columns.Add("HomeID", typeof(int));
                mTable.Columns.Add("HomeScore", typeof(int));
                mTable.Columns.Add("AwayTeam", typeof(string));
                mTable.Columns.Add("AwayID", typeof(int));
                mTable.Columns.Add("AwayScore", typeof(int));
                mTable.Columns.Add("MatchStatURL", typeof(string));

                if (!string.IsNullOrEmpty(date))
                {
                    idt = DateTime.Parse(date);
                }

                HtmlNodeCollection rows = tbl.SelectNodes(".//tr");

                if (rows == null)
                {
                    logger.Debug("#GetMatchList# No match in match table.");
                    return true;
                }

                foreach (HtmlNode tr in rows)
                {
                    HtmlNode tc = tr.SelectSingleNode("./td[@class='t0']");

                    if (tc != null)
                    {
                        if (edt.HasValue)
                        {
                            if (idt.HasValue && edt.Value < idt.Value)
                            {
                                continue;
                            }
                            else
                            {
                                string temp;
                                DataRow dr = mTable.NewRow();

                                dr["MatchTime"] = DateTime.Parse(string.Format("{0} {1}", edt.Value.ToString("yyyy-MM-dd"), tc.InnerText));

                                temp = tr.SelectSingleNode("./td[@class='t1']").InnerText;
                                dr["HomeTeam"] = HtmlEntity.DeEntitize(temp);

                                //dr["HomeID"] = GetID(tr.TableCells[1].Links[0].GetAttributeValue("href"));
                                temp = tr.SelectSingleNode("./td[@class='t2']").InnerText;
                                string[] tempStrs = temp.Split('-');
                                
                                if (tempStrs.Length != 2)
                                {
                                    throw new Exception("Can not parse match score.");
                                }

                                dr["HomeScore"] = int.Parse(tempStrs[0].Trim());
                                
                                temp = tr.SelectSingleNode("./td[@class='t3']").InnerText;
                                dr["AwayTeam"] = HtmlEntity.DeEntitize(temp);
                                
                                //dr["AwayID"] = GetID(tr.TableCells[3].Links[0].GetAttributeValue("href"));
                                dr["AwayScore"] = int.Parse(tempStrs[1].Trim());
                                
                                dr["MatchStatURL"] = tr.SelectSingleNode("./td[@class='t4']/a").GetAttributeValue("href", string.Empty);

                                mTable.Rows.Add(dr);
                            }
                        }
                        else
                        {
                            throw new Exception("Event time has no value.");
                        }
                    }
                    else
                    {
                        string str = tr.InnerText;
                        edt = Translate.TransFrDateTime(str);
                    }
                }

                logger.Debug("#GetMatchList# End");

                return true;
            }
            catch (Exception ex)
            {
                logger.Error("#GetMatchList# Failed: Exception.");
                logger.Error(ex.ToString());
                mTable = null;
                return false;
            }
        }
       
        public int GetID(string str)
        {
            //string temp = str.Remove(0, @"http://www.foot-national.com/".Length);
            string temp = str.Remove(str.IndexOf('-'));
            return int.Parse(temp);
        }
      
        public bool GetMatchStat(HtmlDocument doc, ref MatchData md)
        {
            try
            {
                HtmlNode homeTable = doc.DocumentNode.SelectSingleNode("//table[@class='table equipe1']");
                HtmlNode awayTable = doc.DocumentNode.SelectSingleNode("//table[@class='table equipe2']");
                if (homeTable == null || awayTable == null)
                {
                    throw new Exception("The table of team data is not found");
                }
                logger.Debug("#GetMatchStat# Get home team statistic data.");
                GetTeamStat(homeTable, true, ref md.match, ref md.homeplayers);
                logger.Debug("#GetMatchStat# Get away team statistic data.");
                GetTeamStat(awayTable, false, ref md.match, ref md.awayplayers);
                return true;
            }
            catch (Exception ex)
            {
                logger.Error("#GetMatchStat# Failed: Exception");
                logger.Error(ex.ToString());
                return false;
            }
        }
       
        public bool GetTeamStat(HtmlNode table, bool isHomeTeam, ref MatchStat match, ref Dictionary<int, PlayerStat> players)
        {
            int flag = 0;
            HtmlNodeCollection rows = table.SelectNodes("./tr");

            if (rows == null)
            {
                return true;
            }

            foreach (HtmlNode tr in rows)
            {
                if (tr.InnerText == "JOUEURS")
                {
                    flag = 1;
                    continue;
                }
                else if (tr.InnerText == "REMPLAÇANTS")
                {
                    flag = 2;
                    continue;
                }
                else if (tr.InnerText == "ENTRAINEUR")
                {
                    flag = 3;
                    continue;
                }

                int playerNumber = -1;
                PlayerStat player = null;

                switch (flag)
                {
                    case 1://For top player
                        if (GetPlayerStat(tr, out playerNumber, out player, false))
                        {
                            players.Add(playerNumber, player);
                            if (isHomeTeam)
                            {
                                match.homeYCard = match.homeYCard + player.yellowCard;
                                match.homeRCard = match.homeRCard + player.redCard;
                            }
                            else
                            {
                                match.awayYCard = match.awayYCard + player.yellowCard;
                                match.awayRCard = match.awayRCard + player.redCard;
                            }
                        }
                        else
                        {
                            throw new Exception(string.Format("Error to get the player statistic {0}", tr.InnerText));
                        }

                        break;
                    case 2://For brench player
                        if (GetPlayerStat(tr, out playerNumber, out player, true))
                        {
                            players.Add(playerNumber, player);
                            if (isHomeTeam)
                            {
                                match.homeYCard = match.homeYCard + player.yellowCard;
                                match.homeRCard = match.homeRCard + player.redCard;
                            }
                            else
                            {
                                match.awayYCard = match.awayYCard + player.yellowCard;
                                match.awayRCard = match.awayRCard + player.redCard;
                            }
                        }
                        else
                        {
                            throw new Exception(string.Format("Error to get the player statistic {0}", tr.InnerText));
                        }

                        break;
                    case 3://For coach
                        continue;
                    default:
                        continue;
                }
            }

            return true;
        }
        
        public bool GetPlayerStat(HtmlNode tr, out int number, out PlayerStat player, bool isSubPlayer)
        {
            try
            {
                number = -1;
                player = new PlayerStat();
                
                // validate tr
                HtmlNode tc = tr.SelectSingleNode("./td[@class='t2b']");

                if (tc == null || tr.ChildNodes.Count != 4)
                {
                    return false;
                }

                player.isPlayerSub = isSubPlayer;
                number = GenPlayerNumber();
                player.playerID = GetID(tr.ChildNodes[2].SelectSingleNode("./a").GetAttributeValue("href",string.Empty));
                player.playerName = HtmlEntity.DeEntitize(tr.ChildNodes[2].SelectSingleNode("./a").InnerText);
                
                //Check the card, if substitued, if switchout, if goal
                if (!GetPlayerMinStat(ref player, tr.ChildNodes[2]))
                {
                    return false;
                }
                
                player.markID = Marking.GetMarkingID(player.isPlayerSub, player.isSwitchOut);
                player.posID = PlayerPos.ParsePosIDFr(tr.ChildNodes[3].InnerText);
                
                return true;
            }
            catch (Exception ex)
            {
                number = -1;
                player = null;
                logger.Error("#GetPlayerStat# Failed: Exception");
                logger.Error(ex.ToString());
                return false;
            }
        }

        public int ParseIco(string str)
        {
            string temp = str.Substring(str.LastIndexOf('/') + 1);
            switch (temp)
            {
                case "ico_but.png":
                    return 1; //Goal
                case "ico_butannule.png":
                    return 2; //Own Goal
                case "ico_jaune.png":
                    return 3; //Yellow Card 
                case "ico_rouge.png":
                    return 4; //Red Card
                case "ico_sortie.png":
                    return 5; //Switch out
                case "ico_entree.png":
                    return 6; //Substitution
                default:
                    return -1;
            }
        }

        public int GenPlayerNumber()
        {
            return ((tempNumber++) % 30);
            //try
            //{
            //    return int.Parse(str);
            //}
            //catch (Exception ex)
            //{
            //    return -1 * ((tempNumber++) % 30);
            //}
        }

        public int ParseMin(string str)
        {
            return int.Parse(str.Trim().Replace("'", ""));
        }

        public bool GetPlayerMinStat(ref PlayerStat player, HtmlNode tc)
        {
            try
            {
                HtmlNodeCollection imgNodes = tc.SelectNodes("./img");

                if (imgNodes == null)
                {
                    return true;
                }

                foreach (HtmlNode img in imgNodes)
                {
                    switch (ParseIco(img.Attributes["src"].Value))
                    {
                        case 1://Goal
                            MinuteStat ms = new MinuteStat();
                            ms.goalMin = ParseMin(img.NextSibling.InnerText);
                            ms.isOwnGoal = false;
                            player.goal = player.goal + 1;
                            player.MinStat.Add(ms);
                            break;
                        case 2://Own Goal
                            MinuteStat oms = new MinuteStat();
                            oms.goalMin = ParseMin(img.NextSibling.InnerText);
                            oms.isOwnGoal = true;
                            player.owngoal = player.owngoal + 1;
                            player.MinStat.Add(oms);
                            break;
                        case 3://Yellow Card 
                            player.yCardMin1 = ParseMin(img.NextSibling.InnerText);
                            player.yellowCard = player.yellowCard + 1;
                            break;
                        case 4://Red Card 
                            player.rCardMin = ParseMin(img.NextSibling.InnerText);
                            player.redCard = player.redCard + 1;
                            break;
                        case 5://Substitued
                            player.isSwitchOut = true;
                            break;
                        case 6://Entried
                            player.isPlayerSub = true;
                            break;
                        default:
                            logger.ErrorFormat("Unknow ICO {0}", img.Attributes["src"].Value);
                            break;
                    }
                }

                return true;
            }
            catch (Exception ex)
            {
                logger.Error("#GetPlayerMinStat# Failed: Exception");
                logger.Error(ex.ToString());
                return false;
            }
        }
    }
}
